﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data.Common;
using System.Data;
using System.IO;
using System.Collections.Specialized;

namespace Normal
{
    public class NormalDump
    {
        private string _connectionString;
        private string _providerName;

        /// <summary>
        /// Constructor that expects ConnectionString and ProviderName
        /// </summary>
        /// <param name="ConnectionString"></param>
        /// <param name="ProviderName"></param>
        public NormalDump(string ConnectionString, string ProviderName)
        {
            _connectionString = ConnectionString;
            _providerName = ProviderName;
        }

        /// <summary>
        /// Generates the VOs for All tables
        /// </summary>
        /// <param name="OutputDir"></param>
        /// <param name="GenerateEachFile"></param>
        /// <param name="VOName"></param>
        public void Generate(string OutputDir, bool GenerateEachFile, string VOName, string NameSpace, bool ColumnHasPrefix)
        {
            string NameSpaceStart = "namespace " + NameSpace + "{";
            string NameSpaceEnd = "}";
            StringBuilder strB = new StringBuilder();
            DbProviderFactory f = DbProviderFactories.GetFactory(_providerName);
            DbConnection cnn = f.CreateConnection();
            cnn.ConnectionString = _connectionString;
            cnn.Open();
            try
            {
                DataTable tbSchema = cnn.GetSchema("Tables");
                DataTable tbColumns = cnn.GetSchema("Columns");
                foreach (DataRow schRow in tbSchema.Rows)
                {
                    if (GenerateEachFile)
                    {
                        StartNameSpace(strB, NameSpaceStart);
                    }
                    string sClassName = NormalizateClassName(schRow["TABLE_NAME"].ToString());
                    strB.AppendLine("\t[Serializable]");
                    strB.AppendLine("\t[Table(TableName=\"" + schRow["TABLE_SCHEMA"] + "." + schRow["TABLE_NAME"] + "\")]");
                    strB.AppendLine("\tpublic class " + sClassName + " : ContextBoundObject");
                    strB.AppendLine("{");
                    foreach (DataRow colRow in tbColumns.Rows)
                    {
                        if (schRow["TABLE_SCHEMA"].ToString() == colRow["TABLE_SCHEMA"].ToString()
                            && schRow["TABLE_NAME"].ToString() == colRow["TABLE_NAME"].ToString())
                        {
                            strB.AppendLine("\t\tprivate " + DotNetType(colRow["DATA_TYPE"].ToString(),
                                (colRow["IS_NULLABLE"].ToString() == "YES")) +
                                " " + NormalizeColumnNameField(colRow["COLUMN_NAME"].ToString(), ColumnHasPrefix));
                        }
                    }
                    foreach (DataRow colRow in tbColumns.Rows)
                    {
                        if (schRow["TABLE_SCHEMA"].ToString() == colRow["TABLE_SCHEMA"].ToString()
                            && schRow["TABLE_NAME"].ToString() == colRow["TABLE_NAME"].ToString())
                        {
                            strB.AppendLine("");
                            strB.Append("\t\t[Column(Name=\"" + colRow["COLUMN_NAME"].ToString() + "\"");
                            if ((colRow["IS_NULLABLE"].ToString() == "YES"))
                            {
                                strB.AppendLine(")]");
                            }
                            else
                            {
                                strB.AppendLine(", Required = true)]");
                            }
                            strB.AppendLine("\t\tpublic " + DotNetType(colRow["DATA_TYPE"].ToString(),
                                (colRow["IS_NULLABLE"].ToString() == "YES")) +
                                " " + NormalizeColumnNameProperty(colRow["COLUMN_NAME"].ToString(), ColumnHasPrefix) + " {");
                            strB.AppendLine("\t\t\tget { return " + NormalizeColumnNameField(colRow["COLUMN_NAME"].ToString(), ColumnHasPrefix) + "; }");
                            strB.AppendLine("\t\t\tset { " + NormalizeColumnNameField(colRow["COLUMN_NAME"].ToString(), ColumnHasPrefix) + " = value; }");
                            strB.AppendLine("\t\t}");
                        }
                    }
                    strB.AppendLine("\t}");
                    if (GenerateEachFile)
                    {
                        EndNameSpace(strB, NameSpaceEnd);
                        StreamWriter stm = new StreamWriter(OutputDir + @"\" + sClassName + ".cs");
                        stm.Write(strB.ToString());
                        stm.Close();
                        strB.Remove(0, strB.Length);
                    }
                }
            }
            finally
            {
                if (cnn.State == ConnectionState.Open)
                {
                    cnn.Close();
                }
                cnn = null;
            }
        }

        private string NormalizeColumnNameProperty(string ColumnName, bool ColumnHasPrefix)
        {
            return ColumnName;
        }

        private string DotNetType(string ColumnType, bool Nullable)
        {
            NameValueCollection NonNullable = new NameValueCollection();
            NonNullable.Add("bigint", "Int64");
            NonNullable.Add("binary", "object");
            NonNullable.Add("bit", "bool");
            NonNullable.Add("char", "string");
            NonNullable.Add("datetime", "DateTime");
            NonNullable.Add("float", "double");
            NonNullable.Add("decimal", "decimal");
            NonNullable.Add("numeric", "decimal");
            NonNullable.Add("int", "Int32");
            NonNullable.Add("nchar", "string");
            NonNullable.Add("nvarchar", "string");
            NonNullable.Add("smallint", "Int16");
            NonNullable.Add("sql_variant", "object");
            NonNullable.Add("text", "string");
            NonNullable.Add("tinyint", "Int16");
            NonNullable.Add("uniqueidentifier", "object");
            NonNullable.Add("varbinary", "object");
            NonNullable.Add("varchar", "string");
            NonNullable.Add("xml", "string");
            NonNullable.Add("money", "decimal");
            NonNullable.Add("smallmoney", "decimal");

            NameValueCollection _Nullable = new NameValueCollection();
            _Nullable.Add("binary", "object");
            _Nullable.Add("char", "string");
            _Nullable.Add("decimal", "decimal");
            _Nullable.Add("numeric", "decimal");
            _Nullable.Add("nchar", "string");
            _Nullable.Add("nvarchar", "string");
            _Nullable.Add("sql_variant", "object");
            _Nullable.Add("text", "string");
            _Nullable.Add("uniqueidentifier", "object");
            _Nullable.Add("varbinary", "object");
            _Nullable.Add("varchar", "string");
            _Nullable.Add("xml", "string");
            _Nullable.Add("money", "decimal");
            _Nullable.Add("smallmoney", "decimal");
            _Nullable.Add("bigint", "Int64?");
            _Nullable.Add("bit", "bool?");
            _Nullable.Add("datetime", "DateTime?");
            _Nullable.Add("float", "double?");
            _Nullable.Add("int", "Int32?");
            _Nullable.Add("smallint", "Int16?");
            _Nullable.Add("tinyint", "Int16?");

            if (Nullable)
            {
                ColumnType = _Nullable.Get(ColumnType.ToLower());
            }
            else
            {
                ColumnType = NonNullable.Get(ColumnType.ToLower());
            }
            return ColumnType;
        }

        private string NormalizeColumnNameField(string ColumnName, bool ColumnHasPrefix)
        {
            return ColumnName;
        }

        protected void StartNameSpace(StringBuilder strB, string NameSpaceStart)
        {
            strB.AppendLine("using System;");
            strB.AppendLine("using Normal;");
            strB.AppendLine("using System.Collections.Generic;");
            strB.AppendLine("");
            strB.AppendLine(NameSpaceStart);
        }

        protected void EndNameSpace(StringBuilder strB, string NameSpaceEnd)
        {
            strB.AppendLine(NameSpaceEnd);
        }

        protected string NormalizateClassName(string TableName)
        {
            string[] Items = TableName.Split(new string[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
            string Retorno = "";
            foreach (string Item in Items)
            {
                string sItem = Item.ToLower();
                sItem = sItem.Substring(0, 1).ToUpper() + sItem.Substring(1);
                Retorno += sItem;
            }
            return Retorno;
        }
    }
}
